package com.car.number.utils;

import com.car.number.entity.ImagePart;
import com.car.number.entity.MatchResult;
import org.springframework.util.CollectionUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.ArrayList;

public class ImageUtils {


    public static int colorOffset = 15;

    public static List<Point> matchLocations(byte[] slider, byte[] background) {
        ByteArrayInputStream partImageInputStream = new ByteArrayInputStream(slider);
        ByteArrayInputStream backgroundImageInputStream = new ByteArrayInputStream(background);
        ImagePart partPoints = getPartPoints(partImageInputStream);
        List<Point> points = matchLocations(backgroundImageInputStream, partPoints);
        if (CollectionUtils.isEmpty(points)) {
            colorOffset = 40;
            backgroundImageInputStream = new ByteArrayInputStream(background);
            points = matchLocations(backgroundImageInputStream, partPoints);
            colorOffset = 15;
        }
        return points;
    }


    /**
     * 获取轮廓边缘点
     *
     * @param imageInputStream
     * @return
     */
    private static ImagePart getPartPoints(InputStream imageInputStream) {
        try {
            ImagePart imagePart = new ImagePart();
            List<List<Point>> partPoints = new ArrayList<>();
            BufferedImage bufferedImage = ImageIO.read(imageInputStream);
            int width = bufferedImage.getWidth();
            int height = bufferedImage.getHeight();
            imagePart.setWidth(width);
            imagePart.setHeight(height);
            imagePart.setBufferedImage(bufferedImage);
            for (int y = 0; y < height - 2; y++) {
                List<Point> locations = new ArrayList<>();
                for (int x = 0; x < width; x++) {
                    Color color = new Color(bufferedImage.getRGB(x, y), true);
                    if (255 - color.getAlpha() > 5) {
                        continue;
                    }
                    Point point = new Point(x, y);
                    locations.add(point);
                }
                if (CollectionUtils.isEmpty(locations)) {
                    continue;
                }
                partPoints.add(locations);
            }

            imagePart.setPartPoints(filterPoint(partPoints));

            return imagePart;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static List<List<Point>> filterPoint(List<List<Point>> partPoints) {
        if (CollectionUtils.isEmpty(partPoints)) {
            return partPoints;
        }
        List<List<Point>> newPartPoints = new ArrayList<>();
        for (int i = 0; i < partPoints.size() - 2; i++) {
            if (i < 2 || i > partPoints.size() - 2) {
                continue;
            }
            List<Point> rows = partPoints.get(i);
            List<Point> points = new ArrayList<>();
            for (int j = 2; j < rows.size() - 2; j++) {
                points.add(rows.get(j));
            }
            newPartPoints.add(points);
        }
        return newPartPoints;
    }

    private static int getMinX(List<List<Point>> partPoints) {
        int minX = 1000;
        for (List<Point> rows : partPoints) {
            if (CollectionUtils.isEmpty(rows)) {
                continue;
            }
            Point point = rows.get(0);
            if (point.getX() < minX) {
                minX = (int) point.getX();
            }
        }
        return minX;
    }

    private static List<Point> matchLocations(InputStream imageInputStream, ImagePart imagePart) {
        List<List<Point>> partPoints = imagePart.getPartPoints();
        int minX = getMinX(partPoints);
        Point point = partPoints.get(0).get(0);
        int offsetX = 0;
        if (point.getX() - minX > 5) {
            offsetX = (int) (point.getX() - minX);
        }
        List<Point> locations = new ArrayList<>();
        try {
            BufferedImage bufferedImage = ImageIO.read(imageInputStream);
            int width = bufferedImage.getWidth();
            int height = bufferedImage.getHeight();
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    Color color = new Color(bufferedImage.getRGB(x, y));
                    if (isWhite(color)) {
                        continue;
                    }
                    if (isSkip(x - offsetX, y, imagePart.getWidth(), imagePart.getHeight(), locations)) {
                        continue;
                    }
                    MatchResult result = matchLocation(x, y, width, height, bufferedImage, partPoints);
                    if (result.isAllMatch()) {
                        //匹配率<0.9
                        BigDecimal divide = BigDecimal.valueOf(result.getCountPass()).divide(BigDecimal.valueOf(result.getCountPass() + result.getCountFail()), 2, RoundingMode.HALF_UP);
                        if (divide.compareTo(BigDecimal.valueOf(0.9)) < 0) {
                            continue;
                        }
                        locations.add(new Point((int) (x - point.getX()), (int) (y - point.getY())));
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return locations;
    }

    private static boolean isWhite(Color color) {
        boolean isWhite = 255 - color.getRed() > colorOffset || 255 - color.getGreen() > colorOffset || 255 - color.getBlue() > colorOffset;
        return isWhite;
    }

    private static boolean isSkip(int x, int y, int partWidth, int partHeight, List<Point> locations) {
        int offset = 10;
        for (Point point : locations) {
            if (x >= point.getX() - offset && x <= point.getX() + partWidth + offset && y >= point.getY() - offset && y <= point.getY() + partHeight + offset) {
                return true;
            }
        }
        return false;
    }

    private static MatchResult matchLocation(int x, int y, int width, int height, BufferedImage bufferedImage, List<List<Point>> partPoints) {
        int countPass = 0;
        int countFail = 0;
        boolean isAllMatch = true;
        int startX = (int) partPoints.get(0).get(0).getX();
        loop:
        for (int i = 0; i < partPoints.size(); i++) {
            List<Point> rows = partPoints.get(i);
            Point point = rows.get(0);
            int offsetX = (int) (point.getX() - startX);
            for (int j = 0; j < rows.size(); j++) {
                if (x + j + offsetX >= width || x + j + offsetX < 0 || y + i >= height || y + i < 0) {
                    isAllMatch = false;
                    break loop;
                }
                Color colorPoint = new Color(bufferedImage.getRGB(x + j + offsetX, y + i));
                if (isWhite(colorPoint)) {
                    countFail++;
                } else {
                    countPass++;
                }
            }
        }
        MatchResult result = new MatchResult();
        result.setAllMatch(isAllMatch);
        result.setCountPass(countPass);
        result.setCountFail(countFail);
        return result;
    }
}
